﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Threading;
using System.Windows.Forms;

namespace MyFreecell
{
    class Game
    {
        //绘图用参数
        public static int XiaoJianGe;//下方牌与牌之间的间隔，放牌空位之间的间隔
        public static int DaJianGe; //放牌位与收牌位中间间隔
        public static int PaiGao;//牌的高
        public static int PaiKuan;//牌的宽
        public static int DingJianGe;//放牌收牌位与菜单栏之间间隔
        public static int ZhongJianGe;//中间间隔（放牌位与下面操作牌之间间隔）
        public static int PaiJianGe;//下面放牌的上下牌间隔;

        //控件参数
        Panel[] FangPaiPanel = new Panel[4];//可腾挪临时中转放牌位置
        Panel[] ShouPaiPanel = new Panel[4];//最终牌收放位置
        Poker[] PaiTotal = new Poker[PaiNumAll];//52张牌
        PictureBox king;//king头像

        //功能参数
        public const int PaiNumAll = 52;//总牌数目
        public int ShengYuPaiNum = 52; //当前需要消除牌数，每收一张则减一
        const int PaiListNum = 16;//下方共八列，上方放牌四列（每列只能放一张牌），收牌四列：共16列
        public ArrayList[] PaiList = new ArrayList[PaiListNum];//用于存储牌

        //位置参数
        Point[] destination = new Point[PaiListNum];//可能目的点坐标(牌左上方点)
        Point newLocation = new Point();//记录拖动后牌的位置
        Point curPaiLocation = new Point();//释放牌时，牌的位置

        /// <summary>
        /// 初始参数计算
        /// </summary>
        /// <param name="size"></param>
        public void ChuShiCanShu(Size size)
        {
            PaiKuan = size.Width / 11;
            PaiGao = 150 * PaiKuan / 105;

            XiaoJianGe = PaiKuan / 8;
            DingJianGe = 2 * XiaoJianGe;
            DaJianGe = PaiKuan;
            PaiJianGe = PaiGao / 4;
            ZhongJianGe = PaiGao / 4;

            ShengYuPaiNum = 52;

            //初始化list
            for (int i = 0; i < PaiListNum; i++)
            {
                PaiList[i] = new ArrayList();
            }

            //初始化destination
            for (int i = 0; i < PaiListNum; i++)
            {
                destination[i] = new Point();
            }
        }

        public void Draw(Graphics g, Size size, Form form)
        {
            //画背景
            Rectangle rec = new Rectangle(new Point(0, 0), size);
            Brush brush = new SolidBrush(Color.Green);
            g.FillRectangle(brush, rec);

            //画放牌框
            Pen pen = new Pen(Color.WhiteSmoke, 2);
            for (int i = 0; i < FangPaiPanel.Length; i++)
            {
                Point point = new Point(XiaoJianGe * (i + 1) + i * PaiKuan + 10, DingJianGe + 20);
                Size paisize = new Size(PaiKuan, PaiGao);
                Rectangle r = new Rectangle(point, paisize);
                g.DrawRectangle(pen, r);

                //添加放牌panel
                FangPaiPanel[i] = new Panel();
                FangPaiPanel[i].BackColor = Color.Green;
                FangPaiPanel[i].BorderStyle = BorderStyle.FixedSingle;
                FangPaiPanel[i].MouseEnter += new EventHandler(FangPaiPanel_MouseEnter);//进入效果
                FangPaiPanel[i].MouseLeave += new EventHandler(FangPaiPanel_MouseLeave);//离开效果

                FangPaiPanel[i].Location = point;
                FangPaiPanel[i].Width = PaiKuan;
                FangPaiPanel[i].Height = PaiGao;
                form.Controls.Add(FangPaiPanel[i]);

                //画king头像
                if (i == FangPaiPanel.Length - 1)
                {
                    king = new PictureBox();
                    Point point1 = new Point(XiaoJianGe * (i + 1) + (i + 1) * PaiKuan + 58, DingJianGe + 55);
                    king.Size = new Size(PaiGao / 3 + 10, PaiGao / 3 + 10);
                    king.Location = point1;
                    king.Image = Properties.Resources.kingleft;
                    king.SizeMode = PictureBoxSizeMode.StretchImage;//铺满整个box
                    king.BorderStyle = BorderStyle.Fixed3D;
                    form.Controls.Add(king);
                }
            }

            //画收牌框
            for (int i = 0; i < ShouPaiPanel.Length; i++)
            {
                Point point = new Point(XiaoJianGe * (i + 4) + (i + 5) * PaiKuan + 65, DingJianGe + 20);
                Size paisize = new Size(PaiKuan, PaiGao);
                Rectangle r = new Rectangle(point, paisize);
                g.DrawRectangle(pen, r);

                //添加收牌panel
                ShouPaiPanel[i] = new Panel();
                ShouPaiPanel[i].BackColor = Color.Green;
                ShouPaiPanel[i].BorderStyle = BorderStyle.FixedSingle;

                ShouPaiPanel[i].Location = point;
                ShouPaiPanel[i].Width = PaiKuan;
                ShouPaiPanel[i].Height = PaiGao;
                form.Controls.Add(ShouPaiPanel[i]);
            }
        }

        /// <summary>
        /// king头像左右转动
        /// </summary>
        public void KingLeft()
        {
            king.Image = Properties.Resources.kingleft;
        }
        public void KingRight()
        {
            king.Image = Properties.Resources.kingright;
        }

        /// <summary>
        /// 鼠标进入放牌panle效果
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void FangPaiPanel_MouseEnter(object sender, EventArgs e)
        {
            Panel s = (Panel)sender;
            s.BorderStyle = BorderStyle.Fixed3D;
        }

        /// <summary>
        /// 鼠标离开放牌panle效果
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void FangPaiPanel_MouseLeave(object sender, EventArgs e)
        {
            Panel s = (Panel)sender;
            s.BorderStyle = BorderStyle.FixedSingle;
        }

        /// <summary>
        /// 按正确顺序测试
        /// </summary>
        public void testPaiShun()
        {
            for (int i = 0; i < PaiNumAll; i++)
            {
                PaiTotal[i] = new Poker();
                PaiTotal[i].index = i + 1;//牌的序数，与图片对应

                string imageFile = "_" + (i + 1);
                PaiTotal[i].BackImage = (Image)Properties.Resources.back;//背图案
                PaiTotal[i].ForeImage = (Image)Properties.Resources.ResourceManager.GetObject(imageFile);//正图案

                PaiTotal[i].BackgroundImage = (Image)Properties.Resources.ResourceManager.GetObject(imageFile);//翻牌显示
                PaiTotal[i].VALUE = i % 13 + 1;//为牌赋数字值

                if (i < 13)
                {
                    PaiTotal[i].PaiColor = 1;//牌花色为黑桃
                }
                if (i > 13 && i < 26)
                {
                    PaiTotal[i].PaiColor = 2;//牌花色为红桃
                }
                if (i > 26 && i < 39)
                {
                    PaiTotal[i].PaiColor = 3;//牌花色为梅花
                }
                if (i > 39 && i < 52)
                {
                    PaiTotal[i].PaiColor = 4;//牌花色为方块
                }

                //规定牌是否可以移动
                if (i >= 44)
                {
                    PaiTotal[i].CanMove = true;//后八张可移动
                }
                else
                {
                    PaiTotal[i].CanMove = false;//其余不可移动
                }
            }
        }

        /// <summary>
        /// 随机洗牌
        /// </summary>
        public void XiPai()
        {
            ArrayList num = new ArrayList();
            for (int i = 0; i < PaiNumAll; i++)
            {
                num.Add(i);
            }

            // 随机从已有顺序数组表中抽取数据组成新的随机排序数组
            Random random = new Random();
            for (int i = 0; i < PaiNumAll; i++)
            {
                PaiTotal[i] = new Poker();
                int ran = random.Next(0, PaiNumAll - i);
                PaiTotal[i].index = (int)num[ran] + 1;//牌的序数，与图片对应

                string imageFile = "_" + ((int)num[ran] + 1);
                PaiTotal[i].BackImage = (Image)Properties.Resources.back;//背图案
                PaiTotal[i].ForeImage = (Image)Properties.Resources.ResourceManager.GetObject(imageFile);//正图案

                PaiTotal[i].VALUE = (int)num[ran] % 13 + 1;//为牌赋数字值                

                //花色赋值
                if (PaiTotal[i].index <= 13)
                {
                    PaiTotal[i].PaiColor = 1;//颜色为黑桃
                }
                else if (PaiTotal[i].index <= 26 && PaiTotal[i].index >= 14)
                {
                    PaiTotal[i].PaiColor = 2;//颜色为红桃
                }
                else if (PaiTotal[i].index <= 39 && PaiTotal[i].index >= 27)
                {
                    PaiTotal[i].PaiColor = 3;//颜色为梅花
                }
                else
                {
                    PaiTotal[i].PaiColor = 4;//颜色为方块 
                }

                //后八张可移动，其他不可移动
                if (i >= 44)
                {
                    PaiTotal[i].CanMove = true;
                }
                else
                {
                    PaiTotal[i].CanMove = false;
                }
                num.RemoveAt(ran);
            }
        }


        /// <summary>
        /// 发牌
        /// </summary>
        public void FaPai(Form form)
        {
            //52张牌的初始位置
            Point p = new Point((form.Width - PaiKuan) / 2, form.Height - PaiGao - 80);
            for (int i = 0; i < PaiTotal.Length; i++)
            {
                PaiTotal[i].Width = PaiKuan;
                PaiTotal[i].Height = PaiGao;
                PaiTotal[i].Location = p;
                PaiTotal[i].BackgroundImage = Properties.Resources.back;
                PaiTotal[i].BackgroundImageLayout = ImageLayout.Stretch;
                PaiTotal[i].QueryContinueDrag += new QueryContinueDragEventHandler(PaiTotal_QueryContinueDrag);
                form.Controls.Add(PaiTotal[i]);
            }

            //将牌分配到每个列
            for (int i = 0; i < PaiNumAll; i++)
            {
                PaiTotal[i].LieID = i % 8;
                PaiList[i % 8].Add(PaiTotal[i]);
            }
            //显示发牌效果（改变位置）
            for (int i = 0; i < PaiNumAll; i++)
            {
                PaiTotal[i].Location = new Point(PaiKuan + i % 8 * (PaiKuan + XiaoJianGe), DingJianGe + PaiGao + ZhongJianGe + i / 8 * PaiJianGe);
                PaiTotal[i].BackgroundImage = PaiTotal[i].ForeImage;//翻牌显示
                PaiTotal[i].BringToFront();
                Thread.Sleep(50);
            }
        }

        /// <summary>
        /// 收牌到右上角四个收牌位
        /// </summary>
        public void ShouPai()
        {
            Poker APai = new Poker();
            Poker BPai = new Poker();//临时存储当前处理放牌位最上面的牌
            for (int i = 12; i < 16; i++)
            {
                //某放牌位为空,此时只收A
                if (PaiList[i].Count == 0)
                {
                    for (int j = 0; j < 12; j++)
                    {
                        if (PaiList[j].Count > 0)
                        {
                            APai = (Poker)PaiList[j][PaiList[j].Count - 1];//取得待处理牌列的最后一张牌;
                            //判断是否可收
                            if (APai.VALUE == 1)
                            {
                                TuoDong(APai, i);
                            }
                        }
                    }
                }
                //某放牌位不为空
                else
                {
                    for (int j = 0; j < 12; j++)
                    {
                        if (PaiList[j].Count > 0)
                        {
                            APai = (Poker)PaiList[j][PaiList[j].Count - 1];//取得待处理牌列的最后一张牌;
                            BPai = (Poker)PaiList[i][PaiList[i].Count - 1];
                            //判断是否可收
                            if (APai.VALUE == BPai.VALUE + 1 && APai.PaiColor == BPai.PaiColor)
                            {
                                TuoDong(APai, i);
                            }
                        }
                    }
                }
            }
            //判断是否胜利
            if (ShengYuPaiNum == 0)
            {
                FrmWin frm = new FrmWin();
                frm.ShowDialog();
            }
        }

        /// <summary>
        /// 判断指当前牌可能拖动到的目地位置
        /// </summary>
        public void Destination()
        {
            //获得可能目的点坐标(牌左上方点)

            // 0--7 表示下面的列 8-11表示放牌位
            for (int i = 0; i < PaiListNum; i++)
            {
                if (i >= 12 && i < PaiListNum)
                {
                    destination[i] = ShouPaiPanel[i - 12].Location;
                }
                else if (i > 7 && i < 12)
                {
                    destination[i] = FangPaiPanel[i - 8].Location;
                }
                else
                {
                    //该列不为空
                    if (PaiList[i].Count > 0)
                    {
                        Poker ptem = (Poker)PaiList[i][PaiList[i].Count - 1];
                        destination[i] = ptem.Location;
                    }
                    //该列没有牌，位置就为发牌时第一行的位置
                    else
                    {
                        destination[i] = new Point(PaiKuan + i * (PaiKuan + XiaoJianGe), DingJianGe + PaiGao + ZhongJianGe);
                    }
                }
            }
        }

        /// <summary>
        /// 计算两点之间的距离
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public double Distance(Point p1, Point p2)
        {
            return Math.Pow(Math.Pow((p1.X - p2.X), 2) + Math.Pow((p1.Y - p2.Y), 2), 0.5);//计算两点距离
        }

        /// <summary>
        /// 获得实际目的列序号
        /// </summary>
        /// <param name="pai"></param>
        /// <returns></returns>
        public int GetMuDiLieID(Poker pai)
        {
            double[] distance = new double[PaiListNum];
            for (int i = 0; i < PaiListNum; i++)
            {
                //牌当前位置和可能目的点之间的距离
                distance[i] = Distance(curPaiLocation, destination[i]);
            }
            //找距离最小点
            int LieID = 0;
            for (int i = 0; i < PaiListNum; i++)
            {
                if (distance[i] == distance.Min())
                {
                    LieID = i;
                    break;
                }
            }
            return LieID;
        }

        /// <summary>
        /// 判断拖动是否符合游戏规则
        /// </summary>
        /// <param name="pai"></param>
        /// <param name="MuDiHao"></param>
        /// <returns></returns>
        public bool YouXiaoTuoDong(Poker pai, int MuDiHao)
        {
            //MuDiHao 0--7 表示下面的列 8 9 10 11 表示放牌位
            if (pai.LieID == MuDiHao)//目的为原来位置
            {
                return false;
            }

            else
            {
                //目的为下面的八个列
                if (MuDiHao < 8)
                {
                    //目的位置列无牌，为空位时可以移动
                    if (PaiList[MuDiHao].Count == 0)
                    {
                        return true;
                    }

                    Poker ptem = (Poker)PaiList[MuDiHao][PaiList[MuDiHao].Count - 1];//目的位置列最后一张牌
                    int sign = pai.PaiColor + ptem.PaiColor;//和为奇数则异色，为偶数则同色
                    //牌面值value相连且异色（花色号和为奇数）时可以移动
                    if (pai.VALUE + 1 == ptem.VALUE && sign % 2 != 0)
                    {
                        return true;
                    }

                }

                //目的为放牌位
                else if (MuDiHao >= 8 && MuDiHao < 12)
                {
                    //放牌位为空，可以移动
                    if (PaiList[MuDiHao].Count == 0)
                        return true;
                }

                //目的为收牌位
                else
                {
                    //收牌位为空，且拖动牌为A时，可以移动
                    if (PaiList[MuDiHao].Count == 0 && pai.VALUE == 1)
                    {
                        return true;
                    }

                    //收牌位为空，且拖动牌不为A时，不可以移动
                    else if (PaiList[MuDiHao].Count == 0 && pai.VALUE != 1)
                    {
                        return false;
                    }

                    //收牌位不为空
                    else
                    {
                        //拖动牌与收牌位最后一张牌花色相同且牌面数字相连时可以移动
                        Poker ptem = (Poker)PaiList[MuDiHao][PaiList[MuDiHao].Count - 1];
                        if (pai.VALUE == ptem.VALUE + 1 && pai.PaiColor == ptem.PaiColor)
                        {
                            return true;
                        }
                    }
                }
            }
            //其他情况均不能移动
            return false;
        }

        /// <summary>
        /// 实现拖动
        /// </summary>
        /// <param name="pai"></param>
        /// <param name="MuDiHao"></param>传入的牌想要移动至的位置
        public void TuoDong(Poker pai, int MuDiHao)
        {
            //移动处理
            Point newp = new Point();

            //目的位置为下面的八列
            if (MuDiHao < 8)
            {
                //牌位于下面八列
                if (pai.LieID < 8)
                {
                    //如果该目的列现在为空
                    if (PaiList[MuDiHao].Count == 0)
                    {
                        newp = new Point(PaiKuan + MuDiHao * (PaiKuan + XiaoJianGe), DingJianGe + PaiGao + ZhongJianGe);
                    }

                    //该目的列现在不为空
                    else
                    {
                        Poker MudiLiePai = (Poker)PaiList[MuDiHao][PaiList[MuDiHao].Count - 1];
                        MudiLiePai.CanMove = false;
                        newp = new Point(MudiLiePai.Location.X, MudiLiePai.Location.Y + PaiJianGe);
                    }

                    PaiList[pai.LieID].RemoveAt(PaiList[pai.LieID].Count - 1);//原来的列数组中移除该牌
                    if (PaiList[pai.LieID].Count > 0)
                    {
                        Poker YuanBenLiePai = (Poker)PaiList[pai.LieID][PaiList[pai.LieID].Count - 1];
                        YuanBenLiePai.CanMove = true;
                    }
                    //牌储存变化
                    pai.LieID = MuDiHao;
                    PaiList[MuDiHao].Add(pai);
                    pai.Location = newp;
                    pai.Draged = true;
                }

                //牌位于放牌列
                else
                {
                    //如果该目的列现在为空
                    if (PaiList[MuDiHao].Count == 0)
                    {
                        newp = new Point(PaiKuan + MuDiHao * (PaiKuan + XiaoJianGe), DingJianGe + PaiGao + ZhongJianGe);
                    }

                    //该目的列现在不为空
                    else
                    {
                        Poker MudiLiePai = (Poker)PaiList[MuDiHao][PaiList[MuDiHao].Count - 1];
                        MudiLiePai.CanMove = false;
                        newp = new Point(MudiLiePai.Location.X, MudiLiePai.Location.Y + PaiJianGe);
                    }
                    //牌储存变化
                    PaiList[pai.LieID].RemoveAt(PaiList[pai.LieID].Count - 1);
                    pai.LieID = MuDiHao;
                    PaiList[MuDiHao].Add(pai);
                    pai.Location = newp;
                }
            }

            //目的位置为放牌位（左上四列）
            else if (MuDiHao >= 8 && MuDiHao < 12)
            {
                //牌位于下面八列
                if (pai.LieID < 8)
                {
                    newp = FangPaiPanel[MuDiHao - 8].Location;
                    PaiList[pai.LieID].RemoveAt(PaiList[pai.LieID].Count - 1);

                    if (PaiList[pai.LieID].Count > 0)
                    {
                        Poker YuanBenLiePai = (Poker)PaiList[pai.LieID][PaiList[pai.LieID].Count - 1];
                        YuanBenLiePai.CanMove = true;
                    }

                    pai.LieID = MuDiHao;
                    PaiList[MuDiHao].Add(pai);
                    pai.Location = newp;
                }

                //牌位于放牌列
                else
                {
                    newp = FangPaiPanel[MuDiHao - 8].Location;
                    PaiList[pai.LieID].RemoveAt(PaiList[pai.LieID].Count - 1);
                    pai.LieID = MuDiHao;
                    PaiList[MuDiHao].Add(pai);
                    pai.Location = newp;
                }
            }

            //目的为收牌位（右上四列）
            else
            {
                //牌位于下面八列
                if (pai.LieID < 8)
                {
                    pai.CanMove = false;
                    newp = ShouPaiPanel[MuDiHao - 12].Location;
                    PaiList[pai.LieID].RemoveAt(PaiList[pai.LieID].Count - 1);

                    if (PaiList[pai.LieID].Count > 0)
                    {
                        Poker YuanBenLiePai = (Poker)PaiList[pai.LieID][PaiList[pai.LieID].Count - 1];
                        YuanBenLiePai.CanMove = true;
                    }
                    pai.LieID = MuDiHao;
                    PaiList[MuDiHao].Add(pai);
                    pai.Location = newp;
                    pai.BringToFront();
                }

                //牌位于放牌列
                else
                {
                    pai.CanMove = false;
                    newp = ShouPaiPanel[MuDiHao - 12].Location;
                    PaiList[pai.LieID].RemoveAt(PaiList[pai.LieID].Count - 1);

                    pai.LieID = MuDiHao;
                    PaiList[MuDiHao].Add(pai);
                    pai.Location = newp;
                    pai.BringToFront();

                }
                ShengYuPaiNum--;
            }
            Destination();
        }

        /// <summary>
        /// //拖动时，释放鼠标，检查是否有效拖动，无效则牌回到原来位置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void PaiTotal_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
        {
            Poker pai = (Poker)sender;

            if (e.Action == DragAction.Drop)
            {
                int ID = GetMuDiLieID(pai);//实际目的位置序列号
                if (YouXiaoTuoDong(pai, ID))
                {
                    TuoDong(pai, ID);
                    ShouPai();//拖动后，如果有可以放置到收牌框的牌，则收牌
                }
                else
                {
                    pai.Location = pai.PaiTuoQianWeiZhi;//恢复拖前情形 
                }
            }
        }

        /// <summary>
        /// 拖动事件
        /// </summary>
        public void Drag(object sender, DragEventArgs e, Form form)
        {
            //得到拖放对象中的牌  
            Poker pai = e.Data.GetData(typeof(Poker)) as Poker;

            //计算牌相对于From的X，Y坐标,否则直接使用X，Y是屏幕坐标            
            newLocation = form.PointToClient(new Point(e.X, e.Y));
            newLocation.Offset(0 - pai.MouseLocation.X, 0 - pai.MouseLocation.Y);
            pai.Location = newLocation;
            curPaiLocation = newLocation;
        }
    }
}
